/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package treegen;

import events.SpinnerValueChangedEvent;
import events.SpinnerValueChangedListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.PerspectiveCameraBuilder;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Класс приложения. Отвечает за графическое представление виджетов и переключение элементов.
 * @author Елена
 */
public class TreeGen extends Application implements SpinnerValueChangedListener {
    /** Сцена*/
    Scene scene;
    /** Основа*/
    Stage _stage;
    /** Корневой элемент*/
    Group root;
    /** Панель с вкладками*/
    TabPane tabs;
    /** Панель "Ствол"*/
    Tab trunk;
    /** Панель "Листья"*/
    Tab leaves;
    /** Панель "Крона"*/
    Tab crown;
    /** Панель "Ветви"*/
    Tab branch;
    /** Панель с ветвями*/
    BranchPane branchPane;
    /** Панель с листьями*/
    LeafPanel leafPanel;
    /** Панель с кроной*/
    CrownPanel crownPanel;
    /** Панель со стволом*/
    TrunkPanel trunkPanel;
    /** Дерево*/
    Tree tree;
    /** Файл для импорта/экспорта в xml*/
    File docXML;

    @Override
    /**
     * Главный метод. Выполняется при запуске приложения
     */
    public void start(Stage stage) {
        tree = new Tree(this);

        stage.setTitle("TreeGenerator");
        root = new Group();
        stage.getIcons().add(new Image(TreeGen.class.getResourceAsStream("pictures/icon.png")));
        scene = makeScene();
        _stage = stage;
        _stage.setScene(scene);
        _stage.setResizable(false);
        // Создаем виджет со вкладками
        tabs = new TabPane();
        trunk = new Tab();
        trunk.setText("Ствол");
        trunk.setClosable(false);
        branch = new Tab();
        branch.setText("Ветви");
        branch.setClosable(false);
        branchPane = new BranchPane(tree);
        branch.setContent(branchPane.tabCont);
        leaves = new Tab();
        leaves.setText("Листья");
        leaves.setClosable(false);
        leafPanel = new LeafPanel(tree);
        leaves.setContent(leafPanel.mainPane);
        crown = new Tab();
        crown.setText("Крона");
        crown.setClosable(false);
        crownPanel = new CrownPanel(tree);
        crown.setContent(crownPanel.mainPane);
        trunkPanel = new TrunkPanel(tree);
        trunk.setContent(trunkPanel.mainPane);
        tabs.getTabs().addAll(trunk, crown, branch, leaves);
        root.getChildren().add(tabs);
        tabs.setPadding(new Insets(25, 0, 0, 0));
        tabs.setStyle("-fx-background-color: whitesmoke;");
        //root.prefHeightProperty().bind(scene.heightProperty());
        //root.prefWidthProperty().bind(scene.widthProperty());
        final FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Открыть файл");
        fileChooser.getExtensionFilters().addAll(
                new FileChooser.ExtensionFilter("All Files", "*.*"),
                new FileChooser.ExtensionFilter("xml", "*.xml")
        );

        final FileChooser fileChooser1 = new FileChooser();
        fileChooser1.setTitle("Сохранить файл");
        fileChooser1.getExtensionFilters().addAll(
                new FileChooser.ExtensionFilter("All Files", "*.*"),
                new FileChooser.ExtensionFilter("xml", "*.xml")
        );

        final FileChooser fileChooser2 = new FileChooser();
        fileChooser2.setTitle("Сохранить файл");
        fileChooser2.getExtensionFilters().addAll(
                new FileChooser.ExtensionFilter("All Files", "*.*"),
                new FileChooser.ExtensionFilter("png", "*.png")
        );
        // Создание меню
        MenuBar menuBar = new MenuBar();
        menuBar.setMinWidth(950);
        // Создание подменю
        Menu menuItem = new Menu("Файл");
        // Создание пункта меню 
        final MenuItem newFile = new MenuItem("Новый файл", new ImageView(new Image(TreeGen.class.getResourceAsStream("pictures/filenew.png"))));
        newFile.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                //Сбросить имя файла TODO
                branchPane.clean();
                trunkPanel.clean();
                crownPanel.clean();
                leafPanel.clean();
                cleanCanvas();
            }
        });
        final MenuItem open = new MenuItem("Открыть файл", new ImageView(new Image(TreeGen.class.getResourceAsStream("pictures/fileopen.png"))));
        open.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                File file = fileChooser.showOpenDialog(_stage);
                if (file != null) {
                    docXML = file;
                    try {
                        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                        DocumentBuilder builder = factory.newDocumentBuilder();
                        Document doc;
                        doc = builder.parse(docXML);
                        branchPane.clean();
                        branchPane.accordion.getPanes().clear();
                        branchPane.branches.clear();
                        branchPane.branchLevels.clear();
                        tree.branches.clear();
                        Branch.setLevelCount(0);
                        trunkPanel.clean();
                        crownPanel.clean();
                        leafPanel.clean();
                        cleanCanvas();
                        Node root = doc.getFirstChild();
                        open(root);
                        getValues();
                    } catch (Exception ex) {
                    }
                }
            }
        });

        final MenuItem saveFile = new MenuItem("Сохранить файл", new ImageView(new Image(TreeGen.class.getResourceAsStream("pictures/filesave.png"))));
        saveFile.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                try {
                    save();
                } catch (Exception ex) {

                }
            }
        });

        final MenuItem saveAsFile = new MenuItem("Сохранить файл как", new ImageView(new Image(TreeGen.class.getResourceAsStream("pictures/filesaveas.png"))));
        saveAsFile.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                File file = fileChooser1.showSaveDialog(_stage);
                if (file != null) {
                    docXML = file;
                }
                try {
                    save();
                } catch (Exception ex) {

                }
            }
        });

        final MenuItem cleanFile = new MenuItem("Очистить", new ImageView(new Image(TreeGen.class.getResourceAsStream("pictures/editdelete.png"))));
        cleanFile.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                branchPane.clean();
                trunkPanel.clean();
                crownPanel.clean();
                leafPanel.clean();
                cleanCanvas();
            }
        });

        final MenuItem pngFile = new MenuItem("Экспорт в png", new ImageView(new Image(TreeGen.class.getResourceAsStream("pictures/compfile.png"))));
        pngFile.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                SnapshotParameters param = new SnapshotParameters();
                param.setDepthBuffer(true);
                param.setFill(Color.WHITE);
                WritableImage snapshot = root.getChildren().get(root.getChildren().size() - 1).snapshot(param, null);
                BufferedImage tempImg = SwingFXUtils.fromFXImage(snapshot, null);
                File file = fileChooser2.showSaveDialog(_stage);
                if (file != null) {
                    try {
                        ImageIO.write(tempImg, "png", file);
                    } catch (IOException ex) {
                        Logger.getLogger(Trunk.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        });

        menuItem.getItems().addAll(newFile, open, saveFile, saveAsFile, pngFile, cleanFile);
        menuBar.getMenus().add(menuItem);
        menuBar.setMinWidth(960);
        root.getChildren().add(menuBar);
        //tree.trunks.add(new Trunk(trunkPanel, tree));
        // Панель для рисования
        Rectangle r = new Rectangle();
        r.setX(450);
        r.setY(25);
        r.setWidth(510);
        r.setHeight(730);
        r.setFill(Color.GAINSBORO);
        root.getChildren().add(r);
        Rectangle pane = new Rectangle();
        pane.setX(460);
        pane.setY(35);
        pane.setWidth(490);
        pane.setHeight(610);
        pane.setFill(Color.WHITE);
        DropShadow ds = new DropShadow();
        ds.setOffsetY(3.0f);
        ds.setColor(Color.color(0.4f, 0.4f, 0.4f));
        pane.setEffect(ds);
        root.getChildren().add(pane);
        /*Branch b = new Branch(1,1);
         b.texture = TrunkTexture.largeCrack;
         b.type = BranchType.Up;
         b.length = 200.4;
         b.width = 5;*/
        Trunk b = new Trunk(trunkPanel, tree);
        b.texture = TrunkTexture.largeCrack;
        b.width = 100;
        b.divisionCount = 5;
        b.hStartDivision = 400;
        b.height = 500;
        b.shape = 1;
        root.setDepthTest(DepthTest.ENABLE);
        //b.crownType = "П";
        // root.getChildren().add(b.paint(705, 600));
        _stage.show();
    }

    public void cleanCanvas() {
        Rectangle pane = new Rectangle();
        root.getChildren().remove(root.getChildren().size() - 1);
        pane.setX(460);
        pane.setY(35);
        pane.setWidth(490);
        pane.setHeight(610);
        pane.setFill(Color.WHITE);
        DropShadow ds = new DropShadow();
        ds.setOffsetY(3.0f);
        ds.setColor(Color.color(0.4f, 0.4f, 0.4f));
        pane.setEffect(ds);
        root.getChildren().add(pane);
    }

    /**
     * Получить значения.
     */
    public void getValues() {
        boolean success;

        tree.trunks.get(0).divisions.clear();
        int tabNum = tabs.getSelectionModel().getSelectedIndex();

        success = tree.setTrunks();

        if (success) {
            CrownPanel.getValues();

            for (int i = 0; i < branchPane.accordion.getPanes().size(); i++) {
                BranchLevel cur = branchPane.branchLevels.get(i);
                success = success && cur.getValues();
            }
        }

        if (success) {
            LeafPanel.getValues();
            root.getChildren().add(tree.paint());
        }
    }

    /** 
     * Создать сцену
     * @return сцена
     */
    private Scene makeScene() {
        return SceneBuilder.create()
                .width(950)
                .height(650)
                .root(root)
                .camera(PerspectiveCameraBuilder.create()
                        .build())
                .depthBuffer(true)
                .build();
    }

    /**
     * The main() method is ignored in correctly deployed JavaFX application.
     * main() serves only as fallback in case the application can not be
     * launched through deployment artifacts, e.g., in IDEs with limited FX
     * support. NetBeans ignores main().
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    /**
     * Сохранить значения
     */
    public void saveValue(SpinnerValueChangedEvent e) {
        getValues();
    }

    /**
     * Открытие из файла
     *
     * @param node тег в xml файле
     */
    public void open(Node node) {
        String text = node.getNodeName();
        if (text != null && !text.isEmpty()) {
            NodeList children = node.getChildNodes();
            if (text.contains("leaf")) {
                LeafPanel.COMBO_SHAPE.setValue(children.item(1).getAttributes().item(0).getTextContent());
                LeafPanel.COMBO_SIZE.setValue(children.item(3).getAttributes().item(0).getTextContent());
                LeafPanel.COMBO_COLOR.setValue(children.item(5).getAttributes().item(0).getTextContent());
                LeafPanel.getValues();
            } else if (text.contains("trunc")) {
                trunkPanel.widthSpinner.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(1).getAttributes().item(0).getTextContent())));
                trunkPanel.heightSpinner.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(3).getAttributes().item(0).getTextContent())));
                trunkPanel.comboShape.setValue(children.item(5).getAttributes().item(0).getTextContent());
                trunkPanel.comboTrunkCount.setValue(Integer.valueOf(children.item(7).getAttributes().item(0).getTextContent()));
                trunkPanel.hStartBranchesSpinner.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(9).getAttributes().item(0).getTextContent())));
                trunkPanel.divisionCountSpinner.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(11).getAttributes().item(0).getTextContent())));
                trunkPanel.hStartDivisionSpinner.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(13).getAttributes().item(0).getTextContent())));
                trunkPanel.comboTexture.setValue(children.item(15).getAttributes().item(0).getTextContent());
                trunkPanel.build();
                tree.setTrunks();
            } else if (text.contains("branches")) {
                branchPane.combo.setValue(Integer.valueOf(node.getAttributes().item(0).getTextContent()));
                for (int i = 0; i < children.getLength(); i++) {
                    open(children.item(i));
                }
            } else if (text.contains("level")) {
                int number = Integer.valueOf(node.getAttributes().item(1).getTextContent());
                BranchLevel cur = branchPane.branchLevels.get(number);
                cur.heightL.setNumber(BigDecimal.valueOf(Double.valueOf(node.getAttributes().item(0).getTextContent())));
                cur.width.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(1).getAttributes().item(0).getTextContent())));
                cur.length.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(3).getAttributes().item(0).getTextContent())));
                cur.rad.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(5).getAttributes().item(0).getTextContent())));
                cur.angle.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(7).getAttributes().item(0).getTextContent())));
                cur.childCount.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(9).getAttributes().item(0).getTextContent())));
                cur.childRep.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(11).getAttributes().item(0).getTextContent())));
                cur.height.setNumber(BigDecimal.valueOf(Double.valueOf(children.item(13).getAttributes().item(0).getTextContent())));
                cur.combo.setValue(children.item(15).getAttributes().item(0).getTextContent());
                cur.getValues();
            } else if (text.contains("crown")) {
                CrownPanel.COMBO_SHAPE.setValue(children.item(1).getAttributes().item(0).getTextContent());
                CrownPanel.COMBO_DENSITY.setValue(children.item(3).getAttributes().item(0).getTextContent());
                CrownPanel.getValues();
            } else {
                for (int i = 0; i < children.getLength(); i++) {
                    open(children.item(i));
                }
            }
        }
    }

    /**
     * Сохранение в файл
     */
    public void save() throws Exception {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.newDocument();
        if (docXML == null) {
            docXML = new File("test.xml");
        } else {
            docXML = new File(docXML.getPath());
        }

        Element root = doc.createElement("tree");
        doc.appendChild(root);

        Element leafElem = doc.createElement("leaf");

        Element plshape = doc.createElement("plshape");
        plshape.setAttribute("value", Leaf.SHAPE);
        leafElem.appendChild(plshape);

        Element plsize = doc.createElement("plsize");
        plsize.setAttribute("value", Leaf.SIZE);
        leafElem.appendChild(plsize);

        Element plcolor = doc.createElement("plcolor");
        plcolor.setAttribute("value", Leaf.COLOR);
        leafElem.appendChild(plcolor);

        root.appendChild(leafElem);

        Element truncElem = doc.createElement("trunc");

        Element ptwidth = doc.createElement("ptwidth");
        ptwidth.setAttribute("value", Double.toString(tree.trunks.get(0).width));
        truncElem.appendChild(ptwidth);

        Element ptheight = doc.createElement("ptheight");
        ptheight.setAttribute("value", Double.toString(tree.trunks.get(0).height));
        truncElem.appendChild(ptheight);

        Element ptshape = doc.createElement("ptshape");
        ptshape.setAttribute("value", tree.trunks.get(0).strShape);
        truncElem.appendChild(ptshape);

        Element pttrunccount = doc.createElement("pttrunccount");
        pttrunccount.setAttribute("value", Integer.toString(tree.trunks.get(0).truncCount));
        truncElem.appendChild(pttrunccount);

        Element ptbranchheight = doc.createElement("ptbranchheight");
        ptbranchheight.setAttribute("value", Double.toString(tree.trunks.get(0).hStartBranches));
        truncElem.appendChild(ptbranchheight);

        Element ptchildrencount = doc.createElement("ptchildrencount");
        ptchildrencount.setAttribute("value", Integer.toString(tree.trunks.get(0).divisionCount));
        truncElem.appendChild(ptchildrencount);

        Element ptchildrenheight = doc.createElement("ptchildrenheight");
        ptchildrenheight.setAttribute("value", Double.toString(tree.trunks.get(0).hStartDivision));
        truncElem.appendChild(ptchildrenheight);

        Element pttexture = doc.createElement("pttexture");
        pttexture.setAttribute("value", tree.trunks.get(0).strTexture);
        truncElem.appendChild(pttexture);

        root.appendChild(truncElem);

        Element crownElem = doc.createElement("crown");

        Element pcshape = doc.createElement("pcshape");
        pcshape.setAttribute(("value"), Crown.TYPE);
        crownElem.appendChild(pcshape);

        Element pcdencity = doc.createElement("pcdencity");
        pcdencity.setAttribute(("value"), Crown.DEN);
        crownElem.appendChild(pcdencity);

        root.appendChild(crownElem);

        Element brElem = doc.createElement("branches");
        brElem.setAttribute("count", Integer.toString(tree.branches.size()));

        for (int i = 0; i < tree.branches.size(); i++) {
            Element level = doc.createElement("level_" + Integer.toString(i));
            level.setAttribute("height", Double.toString(branchPane.branchLevels.get(i).heightL.getNumber().doubleValue()));
            level.setAttribute("value", Integer.toString(i));

            Element pbwidht = doc.createElement("pbwidht");
            pbwidht.setAttribute("value", Double.toString(tree.branches.get(i).width));
            level.appendChild(pbwidht);

            Element pblength = doc.createElement("pblength");
            pblength.setAttribute("value", Double.toString(tree.branches.get(i).length));
            level.appendChild(pblength);

            Element pbdistance = doc.createElement("pbdistance");
            pbdistance.setAttribute("value", Double.toString(tree.branches.get(i).radByTrunk));
            level.appendChild(pbdistance);

            Element pbangle = doc.createElement("pbangle");
            pbangle.setAttribute("value", Double.toString(tree.branches.get(i).angle));
            level.appendChild(pbangle);

            Element pbchildrencount = doc.createElement("pbchildrencount");
            pbchildrencount.setAttribute("value", Integer.toString(tree.branches.get(i).childrenCount));
            level.appendChild(pbchildrencount);

            Element pbrepcount = doc.createElement("pbrepcount");
            pbrepcount.setAttribute("value", Integer.toString(tree.branches.get(i).repetCount));
            level.appendChild(pbrepcount);

            Element pbheight = doc.createElement("pbheight");
            pbheight.setAttribute("value", Double.toString(tree.branches.get(i).heightByTrunk));
            level.appendChild(pbheight);

            Element pbtype = doc.createElement("pbtype");
            pbtype.setAttribute("value", tree.branches.get(i).strType);
            level.appendChild(pbtype);

            brElem.appendChild(level);

        }

        root.appendChild(brElem);

        Transformer t = TransformerFactory.newInstance().newTransformer();
        t.setOutputProperty(OutputKeys.METHOD, "xml");
        t.setOutputProperty(OutputKeys.INDENT, "yes");
        t.transform(new DOMSource(doc), new StreamResult(docXML));
    }
}
